import React from "react";
import {BaseEventOrig, Image, Textarea, View} from "@tarojs/components";
import {TextareaProps} from "@tarojs/components/types/Textarea";
import classNames from "classnames"
import {ITouchEvent} from "@tarojs/components/types/common";
import Taro from "@tarojs/taro"

import styles from "./InputBar.module.scss"
import {Async} from "../../Async";
import {getSystemInfo, nextTick, querySelectorClientRect} from "../../wxapi";
import {BtnKeyboard, BtnSwitch, BtnVoice} from "../../images";

interface Props {
  onSendText:(text:string)=>void;
  onChangeModeToShowFunctionPad:()=>void;
  onHeightChange:(height:number)=>Promise<void>;
}

interface State {
  text:string;
  autoHeight:boolean;
  mode:Mode,
  inputFocus?:boolean;
  cursorSpacing:number;
}

enum InputBarMode {
  Unkown = "Unkown",
  Idle = "Idle",
  TextInput = "TextInput",
  ShowFunctionPad = "ShowFunctionPad",
  VoiceInput = "VoiceInput",
}

/**
 * 1.点击加号弹出功能板
 * 2.点击加号，当功能版已经弹出时，弹出键盘
 * 3.点击语音，收回功能版，icon切换为键盘，显示语音输入模式
 */
abstract class Mode {
  protected owner:InputBar;

  constructor(owner:InputBar) {
    this.owner = owner;
  }

  abstract value():InputBarMode;
  async changeModeToTextInput():Promise<void> {}
  async changeModeToShowFunctionPad():Promise<void> {}
  async changeModeToVoiceInput():Promise<void> {}
  async changeModeToIdle():Promise<void> {}
}

class InputBarModeUnkown extends Mode {
  value(): InputBarMode {
    return InputBarMode.Unkown;
  }

  async changeModeToIdle():Promise<void> {
    await this.owner.changeModeToIdle();
  }
}

class InputBarModeIdle extends Mode {
  value(): InputBarMode {
    return InputBarMode.Idle;
  }

  async changeModeToShowFunctionPad(): Promise<void> {
    await this.owner.changeModeToShowFunctionPad();
  }

  async changeModeToTextInput():Promise<void> {
    await this.owner.changeModeToTextInput();
  }

  async changeModeToVoiceInput():Promise<void> {
    await this.owner.changeModeToVoiceInput();
  }
}

class InputBarModeTextInput extends Mode {
  value(): InputBarMode {
    return InputBarMode.TextInput;
  }

  async changeModeToShowFunctionPad(): Promise<void> {
    await this.owner.changeModeToShowFunctionPad();
  }

  async changeModeToVoiceInput(): Promise<void> {
    await this.owner.changeModeToVoiceInput();
  }
}

class InputBarModeShowFunctionPad extends Mode {
  value(): InputBarMode {
    return InputBarMode.ShowFunctionPad;
  }

  async changeModeToTextInput():Promise<void> {
    await this.owner.changeModeToTextInput();
  }

  async changeModeToShowFunctionPad(): Promise<void> {
    await this.owner.changeModeToShowFunctionPad();
  }

  async changeModeToVoiceInput(): Promise<void> {
    await this.owner.changeModeToVoiceInput();
  }
}

class InputBarModeVoiceInput extends Mode {
  value(): InputBarMode {
    return InputBarMode.VoiceInput;
  }

  async changeModeToTextInput():Promise<void> {
    await this.owner.changeModeToTextInput();
  }

  async changeModeToShowFunctionPad(): Promise<void> {
    await this.owner.changeModeToShowFunctionPad();
  }
}

export class InputBar extends React.Component<Props, State> {
  constructor(args: Props | Readonly<Props>) {
    super(args);

    this.state = {
      text:"",
      autoHeight:true,
      mode:new InputBarModeUnkown(this),
      cursorSpacing:10, //P20测试数据
    };
  }

  componentDidMount(): void {
    Async(async ()=> {
      await this.state.mode.changeModeToIdle();
    });
  }

  private height:number = 0;
  async changeModeToShowFunctionPad():Promise<void> {
    await this.changeModeTo(new InputBarModeShowFunctionPad(this));
    await this.setInputFocus(false);
    this.props.onChangeModeToShowFunctionPad();
    await this.onHeightChange();
  }

  async onHeightChange():Promise<void> {
    await nextTick();
    let [systemInfo, _] = await getSystemInfo();
    let ret = await querySelectorClientRect("#InputBar");
    const height = systemInfo?.windowHeight!-ret.top;
    if (height<=0) {
      console.warn("onHeightChange height error, height must > 0")
    }

    if (this.height!==height) {
      console.log("onHeightChange", height);
      this.height = height;
      await this.props.onHeightChange(height);
    } else {
      console.log("ignore onHeightChange", height);
    }
  }

  async setInputFocus(inputFocus:boolean):Promise<void> {
    console.log("setInputFocus", inputFocus);
    return new Promise(resolve => {
      if (this.state.inputFocus) {
        Taro.hideKeyboard();
      }
      this.setState({inputFocus:inputFocus}, ()=> {
        resolve();
      })
    })
  }

  async changeModeToTextInput():Promise<void> {
    await this.changeModeTo(new InputBarModeTextInput(this));
    await this.setInputFocus(true);
    let [ret, _] = await getSystemInfo();
    if (ret?.platform=="devtools") {
      await this.onHeightChange();
    }
  }

  onKeyboardHeightChange(e:BaseEventOrig<TextareaProps.onKeyboardHeightChangeEventDetail>):void {
    console.log("keyboard height=", e.detail.height);
    Async(async ()=> {
      await this.onHeightChange()
    })
  }

  async changeModeToVoiceInput():Promise<void> {
    await this.changeModeTo(new InputBarModeVoiceInput(this));
    await this.setInputFocus(false);
    await this.onHeightChange();
  }

  async changeModeToIdle():Promise<void> {
    await this.changeModeTo(new InputBarModeIdle(this));
    await this.setInputFocus(false);
    await this.onHeightChange();
  }

  async changeModeTo(newMode:Mode):Promise<void> {
    console.log("changeModeTo ",this.state.mode.value(), " to ", newMode.value());
    return new Promise(resolve => {
      this.setState({mode:newMode}, ()=> {
        resolve();
      })
    })
  }

  onSendText():void {
    this.props.onSendText(this.state.text);
    this.setState({text:""});
  }

  onTextInput(e:BaseEventOrig<TextareaProps.onInputEventDetail>):void {
    console.log("onTextInput timeStamp", e.timeStamp);
    this.setState({text:e.detail.value});
    Async(async ()=> {
      await this.state.mode.changeModeToTextInput();
    })
  }

  onTextBlur(e:BaseEventOrig<TextareaProps.onBlurEventDetail>):void {
    console.log("onTextBlur", e);
  }

  onTextClick(e:ITouchEvent):void {
    e.stopPropagation();

    Async(async ()=> {
      await this.state.mode.changeModeToTextInput();
    })
  }

  onLineChange(e:BaseEventOrig<TextareaProps.onLineChangeEventDetail>):void {
    this.setState({autoHeight:e.detail.lineCount<=5})
  }
  onSwitchFunctionPad():void {
    Async(async ()=> {
      if (this.state.mode.value() === InputBarMode.ShowFunctionPad) {
        await this.state.mode.changeModeToTextInput();
      } else {
        await this.state.mode.changeModeToShowFunctionPad();
      }
    })
  }
  onSwitchToVoice():void {
    Async(async ()=> {
      await this.state.mode.changeModeToVoiceInput();
    })
  }

  onSwitchToKeyboard():void {
    Async(async ()=> {
      await this.state.mode.changeModeToTextInput();
    })
  }

  render () {
    const emptyStyle = {flex:1};
    const functionPadStyle = {display:this.state.mode.value()===InputBarMode.ShowFunctionPad?'inline':'none'};

    return (
      <View id='InputBar'>
        <View className={classNames(styles.container, 'line_hd')}>
          <View className={styles.left}>
            <View style={emptyStyle} />
              {this.state.mode.value()===InputBarMode.TextInput ?
                <Image onClick={this.onSwitchToVoice.bind(this)}
                  src={BtnVoice} className={styles.btnVoice}
                /> :
                <Image onClick={this.onSwitchToKeyboard.bind(this)}
                  src={BtnKeyboard} className={styles.btnKeyboard}
                />
              }
          </View>
            {this.state.mode.value()!==InputBarMode.VoiceInput?
              (<Textarea onLineChange={this.onLineChange.bind(this)}
                onClick={this.onTextClick.bind(this)}
                onKeyboardHeightChange={this.onKeyboardHeightChange.bind(this)}
                onInput={this.onTextInput.bind(this)}
                onBlur={this.onTextBlur.bind(this)}
                cursorSpacing={this.state.cursorSpacing}
                autoHeight={this.state.autoHeight}
                holdKeyboard
                className={styles.input}
                value={this.state.text}
                focus={this.state.inputFocus}
              />):
              (<View className={styles.voiceInput}>按住 说话</View>)
            }

          <View className={styles.right}>
            {this.state.mode.value()===InputBarMode.TextInput && this.state.text.length ? (
              <View onClick={this.onSendText.bind(this)} className={styles.sendBtn}>发送</View>):(
              <Image onClick={this.onSwitchFunctionPad.bind(this)}
                src={BtnSwitch} className={styles.btnSwitch}
              />
            )}
          </View>
        </View>
        <View style={functionPadStyle}>
          {this.props.children}
        </View>
      </View>
    )
  }
}
